"""Conversation management service."""

from typing import List, Optional, Dict, Any, Tuple
from datetime import datetime, timedelta
import motor.motor_asyncio
from bson import ObjectId
import pymongo

from ..models.conversation_model import (
    Conversation, Message, ConversationStatus, 
    ConversationCategory, MessageRole
)


class ConversationManager:
    """Manages conversation CRUD operations and search functionality."""
    
    def __init__(self, mongodb_client: motor.motor_asyncio.AsyncIOMotorClient):
        """Initialize conversation manager.
        
        Args:
            mongodb_client: MongoDB client instance
        """
        self.mongodb = mongodb_client
        self.db = mongodb_client.ai_writing
        self.conversations = self.db.conversations
        
        # Create indexes
        self._ensure_indexes()
    
    def _ensure_indexes(self):
        """Ensure required indexes are created."""
        # Create indexes asynchronously in background
        self.conversations.create_index("user_id")
        self.conversations.create_index("created_at")
        self.conversations.create_index("tags")
        self.conversations.create_index("status")
        self.conversations.create_index("sharing.share_id", unique=True, sparse=True)
        self.conversations.create_index([("messages.content", pymongo.TEXT)])
    
    async def create_conversation(
        self, 
        user_id: str, 
        title: Optional[str] = None,
        model_preference: str = "gpt-4",
        category: ConversationCategory = ConversationCategory.OTHER
    ) -> str:
        """Create a new conversation.
        
        Args:
            user_id: User ID who owns the conversation
            title: Optional conversation title
            model_preference: Preferred AI model
            category: Conversation category
            
        Returns:
            Created conversation ID
        """
        conversation = Conversation(
            user_id=user_id,
            title=title or f"New Conversation {datetime.now().strftime('%Y-%m-%d %H:%M')}",
            model_preference=model_preference,
            category=category
        )
        
        result = await self.conversations.insert_one(conversation.to_dict())
        return str(result.inserted_id)
    
    async def get_conversation(
        self, 
        conversation_id: str, 
        user_id: str
    ) -> Optional[Conversation]:
        """Get a conversation by ID.
        
        Args:
            conversation_id: Conversation ID
            user_id: User ID for ownership verification
            
        Returns:
            Conversation instance or None if not found
        """
        try:
            doc = await self.conversations.find_one({
                "_id": ObjectId(conversation_id),
                "user_id": user_id,
                "status": {"$ne": ConversationStatus.DELETED.value}
            })
            
            if doc:
                return Conversation.from_dict(doc)
            return None
        except Exception:
            return None
    
    async def list_conversations(
        self,
        user_id: str,
        skip: int = 0,
        limit: int = 20,
        status: Optional[ConversationStatus] = None,
        tags: Optional[List[str]] = None,
        category: Optional[ConversationCategory] = None,
        search_query: Optional[str] = None,
        sort_by: str = "updated_at",
        sort_order: int = -1
    ) -> Tuple[List[Conversation], int]:
        """List conversations with pagination and filtering.
        
        Args:
            user_id: User ID
            skip: Number of documents to skip
            limit: Maximum number of documents to return
            status: Filter by status
            tags: Filter by tags
            category: Filter by category
            search_query: Text search query
            sort_by: Field to sort by
            sort_order: 1 for ascending, -1 for descending
            
        Returns:
            Tuple of (conversations list, total count)
        """
        # Build query
        query: Dict[str, Any] = {
            "user_id": user_id,
            "status": {"$ne": ConversationStatus.DELETED.value}
        }
        
        if status:
            query["status"] = status.value
            
        if tags:
            query["tags"] = {"$in": tags}
            
        if category:
            query["category"] = category.value
            
        if search_query:
            query["$text"] = {"$search": search_query}
        
        # Get total count
        total_count = await self.conversations.count_documents(query)
        
        # Get paginated results
        cursor = self.conversations.find(query).sort(
            sort_by, sort_order
        ).skip(skip).limit(limit)
        
        conversations = []
        async for doc in cursor:
            conversations.append(Conversation.from_dict(doc))
        
        return conversations, total_count
    
    async def update_conversation(
        self,
        conversation_id: str,
        user_id: str,
        title: Optional[str] = None,
        tags: Optional[List[str]] = None,
        category: Optional[ConversationCategory] = None,
        model_preference: Optional[str] = None,
        settings: Optional[Dict[str, Any]] = None
    ) -> bool:
        """Update conversation metadata.
        
        Args:
            conversation_id: Conversation ID
            user_id: User ID for ownership verification
            title: New title
            tags: New tags list
            category: New category
            model_preference: New model preference
            settings: New settings dictionary
            
        Returns:
            True if updated successfully
        """
        update_doc: Dict[str, Any] = {
            "$set": {"updated_at": datetime.utcnow()}
        }
        
        if title is not None:
            update_doc["$set"]["title"] = title
            
        if tags is not None:
            update_doc["$set"]["tags"] = tags
            
        if category is not None:
            update_doc["$set"]["category"] = category.value
            
        if model_preference is not None:
            update_doc["$set"]["model_preference"] = model_preference
            
        if settings is not None:
            update_doc["$set"]["settings"] = settings
        
        result = await self.conversations.update_one(
            {
                "_id": ObjectId(conversation_id),
                "user_id": user_id
            },
            update_doc
        )
        
        return result.modified_count > 0
    
    async def delete_conversation(
        self,
        conversation_id: str,
        user_id: str,
        hard_delete: bool = False
    ) -> bool:
        """Delete a conversation (soft delete by default).
        
        Args:
            conversation_id: Conversation ID
            user_id: User ID for ownership verification
            hard_delete: If True, permanently delete from database
            
        Returns:
            True if deleted successfully
        """
        if hard_delete:
            result = await self.conversations.delete_one({
                "_id": ObjectId(conversation_id),
                "user_id": user_id
            })
            return result.deleted_count > 0
        else:
            # Soft delete
            result = await self.conversations.update_one(
                {
                    "_id": ObjectId(conversation_id),
                    "user_id": user_id
                },
                {
                    "$set": {
                        "status": ConversationStatus.DELETED.value,
                        "updated_at": datetime.utcnow()
                    }
                }
            )
            return result.modified_count > 0
    
    async def archive_conversation(
        self,
        conversation_id: str,
        user_id: str
    ) -> bool:
        """Archive a conversation.
        
        Args:
            conversation_id: Conversation ID
            user_id: User ID for ownership verification
            
        Returns:
            True if archived successfully
        """
        result = await self.conversations.update_one(
            {
                "_id": ObjectId(conversation_id),
                "user_id": user_id
            },
            {
                "$set": {
                    "status": ConversationStatus.ARCHIVED.value,
                    "updated_at": datetime.utcnow()
                }
            }
        )
        return result.modified_count > 0
    
    async def restore_conversation(
        self,
        conversation_id: str,
        user_id: str
    ) -> bool:
        """Restore an archived or soft-deleted conversation.
        
        Args:
            conversation_id: Conversation ID
            user_id: User ID for ownership verification
            
        Returns:
            True if restored successfully
        """
        result = await self.conversations.update_one(
            {
                "_id": ObjectId(conversation_id),
                "user_id": user_id,
                "status": {"$in": [
                    ConversationStatus.ARCHIVED.value,
                    ConversationStatus.DELETED.value
                ]}
            },
            {
                "$set": {
                    "status": ConversationStatus.ACTIVE.value,
                    "updated_at": datetime.utcnow()
                }
            }
        )
        return result.modified_count > 0
    
    async def search_conversations(
        self,
        user_id: str,
        query: str,
        skip: int = 0,
        limit: int = 20
    ) -> Tuple[List[Conversation], int]:
        """Search conversations by content.
        
        Args:
            user_id: User ID
            query: Search query
            skip: Number of documents to skip
            limit: Maximum number of documents to return
            
        Returns:
            Tuple of (conversations list, total count)
        """
        search_query = {
            "user_id": user_id,
            "status": {"$ne": ConversationStatus.DELETED.value},
            "$text": {"$search": query}
        }
        
        # Add text score for relevance sorting
        projection = {"score": {"$meta": "textScore"}}
        
        # Get total count
        total_count = await self.conversations.count_documents(search_query)
        
        # Get paginated results sorted by relevance
        cursor = self.conversations.find(
            search_query, 
            projection
        ).sort([
            ("score", {"$meta": "textScore"}),
            ("updated_at", -1)
        ]).skip(skip).limit(limit)
        
        conversations = []
        async for doc in cursor:
            # Remove the score field before converting
            doc.pop("score", None)
            conversations.append(Conversation.from_dict(doc))
        
        return conversations, total_count
    
    async def get_user_tags(self, user_id: str) -> List[str]:
        """Get all unique tags used by a user.
        
        Args:
            user_id: User ID
            
        Returns:
            List of unique tags
        """
        pipeline = [
            {
                "$match": {
                    "user_id": user_id,
                    "status": {"$ne": ConversationStatus.DELETED.value}
                }
            },
            {"$unwind": "$tags"},
            {"$group": {"_id": "$tags"}},
            {"$sort": {"_id": 1}}
        ]
        
        tags = []
        async for doc in self.conversations.aggregate(pipeline):
            tags.append(doc["_id"])
        
        return tags
    
    async def get_statistics(self, user_id: str) -> Dict[str, Any]:
        """Get user's conversation statistics.
        
        Args:
            user_id: User ID
            
        Returns:
            Statistics dictionary
        """
        pipeline = [
            {
                "$match": {
                    "user_id": user_id,
                    "status": {"$ne": ConversationStatus.DELETED.value}
                }
            },
            {
                "$group": {
                    "_id": None,
                    "total_conversations": {"$sum": 1},
                    "total_messages": {"$sum": "$statistics.total_messages"},
                    "total_tokens": {"$sum": "$statistics.total_tokens"},
                    "total_cost": {"$sum": "$statistics.total_cost"},
                    "categories": {"$push": "$category"},
                    "models": {"$push": "$model_preference"}
                }
            }
        ]
        
        cursor = self.conversations.aggregate(pipeline)
        stats = await cursor.to_list(length=1)
        
        if stats:
            result = stats[0]
            # Count category distribution
            categories = result.pop("categories", [])
            models = result.pop("models", [])
            
            result["category_distribution"] = {}
            for cat in categories:
                result["category_distribution"][cat] = result["category_distribution"].get(cat, 0) + 1
            
            result["model_distribution"] = {}
            for model in models:
                result["model_distribution"][model] = result["model_distribution"].get(model, 0) + 1
            
            return result
        
        return {
            "total_conversations": 0,
            "total_messages": 0,
            "total_tokens": 0,
            "total_cost": 0.0,
            "category_distribution": {},
            "model_distribution": {}
        }